/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * This class provides methods for reading and writing integers of 8-, 16-, 32-
 * or 64-bit long. Integers may be either signed or unsigned
 * (except 64-bit ones which are always signed). <br/>
 * <br/>
 * Methods are named according to the following pattern : <br/>
 * <center><tt>
 *   &lt;read|write&gt;((&lt;8|16|32&gt;&lt;u|s&gt;)|64s)
 * </tt></center> <br/>
 * The purpose of this class is to forward calls to either
 * {@link java.io.DataInput} or {@link java.io.DataOutput} from well-named
 * methods.
 *
 * @see java.io.DataInput
 * @see java.io.DataOutput
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
public final class IO {

    /** Number of bits in a byte. */
    private static final int BYTE_SIZE_IN_BITS = 8;

    /** Mask for byte value of an integer. */
    private static final int BYTE_MASK = 0xFF;

    /**
     * No instance of this class.
     */
    private IO() {
    } // end empty constructor

    /**
     * Reads an unsigned 8-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the unsigned 8-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static int read8u(final DataInput in) throws IOException {
        assert in != null : "null in";
        return in.readUnsignedByte();
    } // end method 'read8u(DataInput)'

    /**
     * Reads a signed 8-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the signed 8-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static int read8s(final DataInput in) throws IOException {
        assert in != null : "null in";
        return in.readByte();
    } // end method 'read8s(DataInput)'

    /**
     * Reads an unsigned 16-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the unsigned 16-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static int read16u(final DataInput in) throws IOException {
        assert in != null : "null in";
        return in.readUnsignedShort();
    } // end method 'read16u(DataInput)'

    /**
     * Reads a signed 16-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the signed 16-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static int read16s(final DataInput in) throws IOException {
        assert in != null : "null in";
        return in.readShort();
    } // end method 'read16s(DataInput)'

    /**
     * Reads an unsigned 32-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the unsigned 32-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static long read32u(final DataInput in) throws IOException {
        assert in != null : "null in";
        return Misc.signedToUnsigned(in.readInt());
    } // end method 'read32u(DataInput)'

    /**
     * Reads a signed 32-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the signed 32-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static int read32s(final DataInput in) throws IOException {
        assert in != null : "null in";
        return in.readInt();
    } // end method 'read32s(DataInput)'

    /**
     * Reads a signed 64-bit integer.
     * @param in data source - should not be <tt>null</tt>
     * @return the signed 64-bit integer read
     * @throws IOException if an error occurs during read
     */
    public static long read64s(final DataInput in) throws IOException {
        assert in != null : "null in";
        return in.readLong();
    } // end method 'read64s(DataInput)'

    /**
     * Writes an unsigned 8-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the unsigned 8-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write8u(final DataOutput out,
                               final int val) throws IOException {
        assert out != null : "null out";
        out.write(val);
    } // end method 'write8u(DataOutput, int)'

    /**
     * Writes a signed 8-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the signed 8-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write8s(final DataOutput out,
                               final int val) throws IOException {
        assert out != null : "null out";
        out.writeByte(val);
    } // end method 'write8s(DataOutput, int)'

    /**
     * Writes an unsigned 16-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the unsigned 16-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write16u(final DataOutput out,
                                final int val) throws IOException {
        assert out != null : "null out";

        out.write(IO.BYTE_MASK & (val >> IO.BYTE_SIZE_IN_BITS));
        out.write(IO.BYTE_MASK & val);
    } // end method 'write16u(DataOutput, int)'

    /**
     * Writes a signed 16-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the signed 16-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write16s(final DataOutput out,
                                final int val) throws IOException {
        assert out != null : "null out";
        out.writeShort(val);
    } // end method 'write16s(DataOutput, int)'

    /**
     * Writes an unsigned 32-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the unsigned 32-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write32u(final DataOutput out,
                                final long val) throws IOException {
        assert out != null : "null out";
        out.writeInt(Misc.unsignedToSigned(val));
    } // end method 'write32u(DataOutput, long)'

    /**
     * Writes a signed 32-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the signed 32-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write32s(final DataOutput out,
                                final int val) throws IOException {
        assert out != null : "null out";
        out.writeInt(val);
    } // end method 'write32s(DataOutput, int)'

    /**
     * Writes a signed 64-bit integer.
     * @param out data destination - should not be <tt>null</tt>
     * @param val the signed 64-bit integer to write
     * @throws IOException if an error occurs during write
     */
    public static void write64s(final DataOutput out,
                                final long val) throws IOException {
        assert out != null : "null out";
        out.writeLong(val);
    } // end method 'write64s(DataOutput, long)'

} // end class 'IO'
